Render this report with
~/spinal_cord_paper/scripts/Gg_devel_scWGCNA_module_analysis_render.sh.
library(Seurat)
Attaching SeuratObject
library(WGCNA)
Loading required package: dynamicTreeCut
Loading required package: fastcluster
Attaching package: ‘fastcluster’
The following object is masked from ‘package:stats’:
hclust
Attaching package: ‘WGCNA’
The following object is masked from ‘package:stats’:
cor
library(tidyr)
library(ggplot2)
library(stringr)
library(patchwork)
library(tidyverse)
Registered S3 methods overwritten by 'dbplyr':
method from
print.tbl_lazy
print.tbl_sql
── Attaching packages ──────────────────────────────────────────────────────────────────────────────────────────────────────────── tidyverse 1.3.1 ──
✔ tibble 3.1.8 ✔ dplyr 1.0.10
✔ readr 1.4.0 ✔ forcats 0.5.1
✔ purrr 0.3.4
── Conflicts ─────────────────────────────────────────────────────────────────────────────────────────────────────────────── tidyverse_conflicts() ──
✖ dplyr::filter() masks stats::filter()
✖ dplyr::lag() masks stats::lag()
library(cowplot)
Attaching package: ‘cowplot’
The following object is masked from ‘package:patchwork’:
align_plots
library(pheatmap)
source("~/Neuraltube/scripts/heatmap4.R")
Load individual seurat and test WGCNA data
The individual data sets are the Day 5 (Gg_D05_ctrl_seurat_070323),
Day 7 (Gg_D07_ctrl_seurat_070323), and Day 10 (Gg_ctrl_1_seurat_070323)
chicken spinal cord sets. The test WGCNA data are the modules calculated
on the integrated data set of all three stages.
se_path <- c("Gg_D05_ctrl_seurat_070323",
"Gg_D07_ctrl_seurat_070323",
"Gg_ctrl_1_seurat_070323")
clust_col <- read.csv("~/spinal_cord_paper/annotations/broad_cluster_marker_colors.csv") %>%
rename(broad = broad_cluster) %>%
select(-marker)
Order of the broad clusters for plotting purposes.
broad_order <- c("progenitors",
"FP",
"RP",
"FP/RP",
"neurons",
"OPC",
"MFOL",
"pericytes",
"microglia",
"blood",
"vasculature"
)
my.ses <- list()
col_table <- list()
ord_levels <- list()
for (i in seq(se_path)) {
# load the data sets
my.se <- readRDS(paste0("~/spinal_cord_paper/data/", se_path[i], ".rds"))
annot <- read.csv(list.files("~/spinal_cord_paper/annotations",
pattern = str_remove(se_path[i], "_seurat_\\d{6}"),
full.names = TRUE))
if(length(table(annot$number)) != length(table(my.se$seurat_clusters))) {
stop("Number of clusters must be identical!")
}
# rename for left join
annot <- annot %>%
mutate(fine = paste(fine, number, sep = "_")) %>%
mutate(number = factor(number, levels = 1:nrow(annot))) %>%
rename(seurat_clusters = number)
# cluster order for vln plots
ord_levels[[i]] <- annot$fine[order(match(annot$broad, broad_order))]
# create index for color coding
col_table[[i]] <- annot %>%
left_join(clust_col, by = "broad") %>%
select(c("fine", "color"))
# add cluster annotation to meta data
my.se@meta.data <- my.se@meta.data %>%
rownames_to_column("rowname") %>%
left_join(annot, by = "seurat_clusters") %>%
mutate(fine = factor(fine, levels = annot$fine)) %>%
column_to_rownames("rowname")
my.ses[[i]] <- my.se
}
names(my.ses) <- c("Gg_D05_ctrl", "Gg_D07_ctrl", "Gg_D10_ctrl")
names(col_table) <- c("Gg_D05_ctrl", "Gg_D07_ctrl", "Gg_D10_ctrl")
names(ord_levels) <- c("Gg_D05_ctrl", "Gg_D07_ctrl", "Gg_D10_ctrl")
rm(my.se, annot)
# The reference WGCNA data. We can have several, if we want to test many at the same time
WGCNA_data = list()
WGCNA_data[[1]] = readRDS("~/spinal_cord_paper/output/Gg_devel_int_scWGCNA_250723.rds")
my.wsub =list()
my.wsub[[1]]= c(1:22)
# the name of each sample, as they appear in my.files and in the metadata of the combined object
my.samplenames = c("Gg_D05_ctrl", "Gg_D07_ctrl", "Gg_D10_ctrl")
# The subset of clusters in each of the corresponding samples
my.subset=list(c(1:24),
c(1:26),
c(1:22))
# This is just to add a little bit more sense to the modules, so that we don't get just a color. Corresponds to WGCNA_data
my.modulenames = list()
my.modulenames[[1]] = c(1:22)
#Subset the seurat objects if needed as defined above
for (i in 1:length(my.ses)) {
my.ses[[i]] = subset(my.ses[[i]], idents = my.subset[[i]])
}
# Take only genes that are present in the all samples
my.dcs = list()
# We go trhough each WGCNA data
for(i in 1:length(WGCNA_data)) {
#We start with complete modules
my.dc = WGCNA_data[[i]]$dynamicCols
#Remove the few genes that are not found in the other datasets
my.dc = my.dc[which(names(my.dc) %in% rownames(my.ses[[1]]@assays$RNA@data))]
my.dc = my.dc[which(names(my.dc) %in% rownames(my.ses[[2]]@assays$RNA@data))]
my.dc = my.dc[which(names(my.dc) %in% rownames(my.ses[[3]]@assays$RNA@data))]
my.dc = my.dc[which(my.dc %in% names(table(WGCNA_data[[i]]$dynamicCols))[my.wsub[[i]]])]
my.dcs[[i]] = my.dc
}
Module gene correlation
Here, we do a correlation matrix / heatmap, to see which cell
clusters group togheter. This helps us to make more detailed
dotplots.
This part of the script can still be used to compare several WGCNA
datasets in parallel.
# broad cluster color table
all_col <- do.call(rbind, col_table) %>%
rownames_to_column("sample") %>%
mutate(sample = substr(sample, 1, 11)) %>%
mutate(sample_celltype = paste(sample, fine, sep = "_")) %>%
select(c("color", "sample_celltype", "sample"))
# take the normalized data, of each single-cell object.
my.datExpr = list()
# Go trough each seurat object
for (i in 1:length(my.ses)) {
my.datExpr[[i]] = data.frame(my.ses[[i]]@assays$RNA@data)
}
# Calculate, for every cell, in every sample, the average expression of each of the modules.
my.MEs = list()
# For each set of WGCNA modules
for (i in 1:length(WGCNA_data)) {
# Make a new list
my.MEs[[i]] = list()
# Populate with data frames from each seurat object
for (j in 1:length(my.ses)) {
# Data frame of average module expression, per cell.
my.MEs[[i]][[j]] = moduleEigengenes(t(my.datExpr[[j]][names(my.dcs[[i]]),]), colors = my.dcs[[i]])
}
}
#Calculate average of expression, per sample and cell cluster
my.modavg = list()
for (i in 1:length(WGCNA_data)) {
my.modavg[[i]] = list()
for (j in 1:length(my.ses)) {
#Make the mean per cell clusters
my.modavg[[i]][[j]] = aggregate(
my.MEs[[i]][[j]]$averageExpr,
list(my.ses[[j]]@meta.data$seurat_clusters),
mean
)
# Give the cell clusters meaningful names
rownames(my.modavg[[i]][[j]]) = paste0(
my.samplenames[j],
"_",
levels(my.ses[[j]]@meta.data$fine)[my.subset[[j]]]
)
}
}
#Gather data to plot
my.wgmat = list()
for (i in 1:length(WGCNA_data)) {
#bind the expression data into one dataframe
my.wgmat[[i]] = data.table::rbindlist(my.modavg[[i]])
#Get rid of the extra column
my.wgmat[[i]] = data.frame(my.wgmat[[i]][,-1])
#Restore the rownames
rownames(my.wgmat[[i]]) = unlist(sapply(my.modavg[[i]], rownames))
}
#Get a dataframe with annotations for all the samples and colors we need.
my.metam <- list()
for (i in seq(my.ses)) {
my.metam[[i]] <- my.ses[[i]][[]]
rownames(my.metam[[i]]) <- paste0(rownames(my.metam[[i]]), "_", i)
}
my.metam <- do.call(rbind, my.metam)
my.metam$orig.ident <- str_replace_all(my.metam$orig.ident, pattern = "Gg_ctrl_1", "Gg_D10_ctrl")
# my.metam$sample_celltype = paste0(substr(my.metam$orig.ident,7,9),"_",my.metam$seurat_clusters)
my.metam$sample_celltype = paste0(my.metam$orig.ident, "_", my.metam$fine)
my.metam <- my.metam %>%
tibble::rownames_to_column(var = "cell_ID") %>%
dplyr::left_join(all_col, by = "sample_celltype") %>%
tibble::column_to_rownames(var = "cell_ID")
# get sample colors
my.colsm = c("grey", "grey30", "black")
names(my.colsm) <- c("Gg_D05_ctrl", "Gg_D07_ctrl", "Gg_D10_ctrl")
#The colors for the samples and clusters. First the closest to the heatmap. Add a white space to easy the eye and make less confussing
my.heatcols =list()
for (i in 1:length(my.wgmat)) {
my.heatcols[[i]] = as.matrix(data.frame(
cluster= as.character(all_col$color[match(rownames(my.wgmat[[i]]), all_col$sample_celltype)]),
"." = "white",
sample= as.character(my.colsm[match(all_col$sample, names(my.colsm))]))
)
}
rm(my.datExpr, my.MEs, my.dcs)
spearman correlation heatmap
annotations
# names and colors for the heatmap annotation
annot_name <- data.frame(
"Celltypes" = all_col$sample_celltype,
"Sample" = all_col$sample,
row.names = all_col$sample_celltype)
pheat_col_table <- do.call(rbind, col_table) %>%
rownames_to_column("sample") %>%
mutate(sample = substr(sample, 1,11)) %>%
mutate(fine = paste(sample, fine, sep = "_"))
# match color table with annotation
pheat_col_table <- pheat_col_table[match(annot_name$Celltypes, pheat_col_table$fine),]
annot_col <- list(
Celltypes = pheat_col_table$color,
Sample = c(Gg_D05_ctrl = "#A4A4A4",
Gg_D07_ctrl = "#515151",
Gg_D10_ctrl = "#000000")
)
names(annot_col[[1]]) <- annot_name$Celltypes

heatmap of module pseudobulk average expression

Load the integrated data set
The integrated data set on which the WGCNA is calculated. We plot it,
split by the original cell types from the three original samples.
my.sec = readRDS("~/spinal_cord_paper/data/Gg_devel_int_seurat_250723.rds")
identical(rownames(my.metam), colnames(my.sec))
[1] TRUE
#Which WGCNA dataset are we using?
my.W = 1
#Choose how many groups of cell clusters we want to use. Based on the distance clustering from above
my.clcl = cutree(hclust(as.dist(1-cor(t(my.wgmat[[my.W]])))), k = 5)
my.metam$sample_celltype <- factor(my.metam$sample_celltype, levels = names(my.clcl))
#Set the identities of the integrated data, to the annotated clusters
my.sec = SetIdent(my.sec, value = my.metam$sample_celltype)
p1 <- DimPlot(
my.sec,
reduction = "tsne",
label = TRUE,
repel = TRUE,
cols = my.heatcols[[1]][,"cluster"],
split.by = "orig.ident"
) +
NoLegend()
p1

Avg. module exp. by stage
tSNE DimPlots showing the average expression of each module by
stage.
for (i in seq(my.ses)) {
# prepare average expression table
tmp <- avg.mod.eigengenes %>%
tibble::rownames_to_column("cell_ID") %>%
dplyr::filter(grepl(paste0("_", i, "$"), cell_ID)) %>%
dplyr::mutate(cell_ID = stringr::str_remove_all(cell_ID, paste0("_", i))) %>%
tibble::column_to_rownames("cell_ID")
identical(rownames(tmp), colnames(my.ses[[i]]))
# add meta data to the seurat objects
my.ses[[i]] <- AddMetaData(my.ses[[i]], tmp)
}
#max and min expression per module (column max)
mod_max <- apply(avg.mod.eigengenes, MARGIN = 2, FUN = max)[module_order]
Error: object 'module_order' not found
AE over time
We plot the average expression of each module in the three stages and
the 5 broad cell type clusters present in all 3 stages.
VlnPlots of avg. module exp. by stage and seurat cluster
colored by module


for (i in seq(my.ses)) {
my.ses[[i]]$seurat_clusters <- factor(
my.ses[[i]]$seurat_clusters,
levels = levels(my.ses[[i]]$seurat_clusters)[as.integer(str_extract(ord_levels[[i]], "\\d{1,2}$"))]
)
}
vplots <- list()
for (i in seq(my.ses)) {
mods <- colnames(my.ses[[i]][[]])[grep("^AE",colnames(my.ses[[i]][[]]))]
vplots[[i]] <- VlnPlot(
my.ses[[i]],
features = mods[module_order],
group.by = "seurat_clusters",
stack = TRUE, flip = TRUE,
cols = substring(mods, 3)[module_order]) +
theme(legend.position = "none") +
geom_hline(yintercept = 0, lty = "dashed")
}
vplots[[1]]
vplots[[2]]
vplots[[3]]

colored by cell type
col_table[[i]]$color[as.integer(str_extract(ord_levels[[i]], "\\d{1,2}$"))]
[1] "#edc919" "#edc919" "#edc919" "#edc919" "#edc919" "#edc919" "#853335" "#99ca3c" "#cd2b91" "#cd2b91" "#cd2b91" "#cd2b91" "#008cb5" "#008cb5" "#008cb5" "#008cb5"
[17] "#008cb5" "#008cb5" "#333399" "#333399" "#f26a2d" "#bebebe"
MN modules
For the figures we specifically select the two MN modules and plot
them as Vln plots.


avg. module exp. by stage
v1 <- VlnPlot(my.sec,
features = mods[module_order],
group.by = "orig.ident")
v1
pdf("~/spinal_cord_paper/figures/Fig_1_AE_by_stage.pdf", height = 20, width = 20)
v1
# Date and time of Rendering
Sys.time()
sessionInfo()
LS0tCnRpdGxlOiAiRGV2ZWxfaW50IFdHQ05BIG1vZHVsZXMgZXhwcmVzc2lvbiBpbiBOVCBENSwgRDcsIGFuZCBEMTAgc2FtcGxlcyIKYXV0aG9yOiAiRmFiaW8gU2FjaGVyIgpkYXRlOiAiMDQuMDguMjAyMyIKb3V0cHV0OgogIGh0bWxfZG9jdW1lbnQ6CiAgICB0b2M6IFRSVUUKICAgIHRvY19mbG9hdDogVFJVRQogIGh0bWxfbm90ZWJvb2s6CiAgICBmaWdfaGVpZ2h0OiA3CiAgICBmaWdfd2lkdGg6IDgKZWRpdG9yX29wdGlvbnM6CiAgY2h1bmtfb3V0cHV0X3R5cGU6IGlubGluZQotLS0KClJlbmRlciB0aGlzIHJlcG9ydCB3aXRoIH4vc3BpbmFsX2NvcmRfcGFwZXIvc2NyaXB0cy9HZ19kZXZlbF9zY1dHQ05BX21vZHVsZV9hbmFseXNpc19yZW5kZXIuc2guCgpgYGB7ciBzZXR1cH0KbGlicmFyeShTZXVyYXQpCmxpYnJhcnkoV0dDTkEpCmxpYnJhcnkodGlkeXIpCmxpYnJhcnkoZ2dwbG90MikKbGlicmFyeShzdHJpbmdyKQpsaWJyYXJ5KHBhdGNod29yaykKbGlicmFyeSh0aWR5dmVyc2UpCmxpYnJhcnkoY293cGxvdCkKbGlicmFyeShwaGVhdG1hcCkKc291cmNlKCJ+L05ldXJhbHR1YmUvc2NyaXB0cy9oZWF0bWFwNC5SIikKCmBgYAoKIyBMb2FkIGluZGl2aWR1YWwgc2V1cmF0IGFuZCB0ZXN0IFdHQ05BIGRhdGEKClRoZSBpbmRpdmlkdWFsIGRhdGEgc2V0cyBhcmUgdGhlIERheSA1IChHZ19EMDVfY3RybF9zZXVyYXRfMDcwMzIzKSwgRGF5IDcgKEdnX0QwN19jdHJsX3NldXJhdF8wNzAzMjMpLCBhbmQgRGF5IDEwIChHZ19jdHJsXzFfc2V1cmF0XzA3MDMyMykgY2hpY2tlbiBzcGluYWwgY29yZCBzZXRzLiBUaGUgdGVzdCBXR0NOQSBkYXRhIGFyZSB0aGUgbW9kdWxlcyBjYWxjdWxhdGVkIG9uIHRoZSBpbnRlZ3JhdGVkIGRhdGEgc2V0IG9mIGFsbCB0aHJlZSBzdGFnZXMuCgpgYGB7ciBkYXRhLXNldHN9CnNlX3BhdGggPC0gYygiR2dfRDA1X2N0cmxfc2V1cmF0XzA3MDMyMyIsCiAgICAgICAgICAgICAiR2dfRDA3X2N0cmxfc2V1cmF0XzA3MDMyMyIsCiAgICAgICAgICAgICAiR2dfY3RybF8xX3NldXJhdF8wNzAzMjMiKQoKY2x1c3RfY29sIDwtIHJlYWQuY3N2KCJ+L3NwaW5hbF9jb3JkX3BhcGVyL2Fubm90YXRpb25zL2Jyb2FkX2NsdXN0ZXJfbWFya2VyX2NvbG9ycy5jc3YiKSAlPiUgCiAgcmVuYW1lKGJyb2FkID0gYnJvYWRfY2x1c3RlcikgJT4lIAogIHNlbGVjdCgtbWFya2VyKQoKYGBgCgpPcmRlciBvZiB0aGUgYnJvYWQgY2x1c3RlcnMgZm9yIHBsb3R0aW5nIHB1cnBvc2VzLgoKYGBge3Igb3JkZXJpbmd9CmJyb2FkX29yZGVyIDwtIGMoInByb2dlbml0b3JzIiwKICAgICAgIkZQIiwKICAgICAgIlJQIiwKICAgICAgIkZQL1JQIiwKICAgICAgIm5ldXJvbnMiLAogICAgICAiT1BDIiwKICAgICAgIk1GT0wiLAogICAgICAicGVyaWN5dGVzIiwKICAgICAgIm1pY3JvZ2xpYSIsCiAgICAgICJibG9vZCIsCiAgICAgICJ2YXNjdWxhdHVyZSIKICAgICAgKQpgYGAKCgpgYGB7ciBzZXVyYXQtb2JqZWN0cy1hbmQtYW5ub3RhdGlvbnN9Cm15LnNlcyA8LSBsaXN0KCkKY29sX3RhYmxlIDwtIGxpc3QoKQpvcmRfbGV2ZWxzIDwtIGxpc3QoKQoKZm9yIChpIGluIHNlcShzZV9wYXRoKSkgewogICMgbG9hZCB0aGUgZGF0YSBzZXRzCiAgbXkuc2UgPC0gcmVhZFJEUyhwYXN0ZTAoIn4vc3BpbmFsX2NvcmRfcGFwZXIvZGF0YS8iLCBzZV9wYXRoW2ldLCAiLnJkcyIpKQogIGFubm90IDwtIHJlYWQuY3N2KGxpc3QuZmlsZXMoIn4vc3BpbmFsX2NvcmRfcGFwZXIvYW5ub3RhdGlvbnMiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcGF0dGVybiA9IHN0cl9yZW1vdmUoc2VfcGF0aFtpXSwgIl9zZXVyYXRfXFxkezZ9IiksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBmdWxsLm5hbWVzID0gVFJVRSkpCiAgCiAgaWYobGVuZ3RoKHRhYmxlKGFubm90JG51bWJlcikpICE9IGxlbmd0aCh0YWJsZShteS5zZSRzZXVyYXRfY2x1c3RlcnMpKSkgewogICAgIHN0b3AoIk51bWJlciBvZiBjbHVzdGVycyBtdXN0IGJlIGlkZW50aWNhbCEiKQogIH0KICAKICAjIHJlbmFtZSBmb3IgbGVmdCBqb2luCiAgYW5ub3QgPC0gYW5ub3QgJT4lIAogICAgbXV0YXRlKGZpbmUgPSBwYXN0ZShmaW5lLCBudW1iZXIsIHNlcCA9ICJfIikpICU+JSAKICAgIG11dGF0ZShudW1iZXIgPSBmYWN0b3IobnVtYmVyLCBsZXZlbHMgPSAxOm5yb3coYW5ub3QpKSkgJT4lIAogICAgcmVuYW1lKHNldXJhdF9jbHVzdGVycyA9IG51bWJlcikgCiAgCiAgIyBjbHVzdGVyIG9yZGVyIGZvciB2bG4gcGxvdHMKICBvcmRfbGV2ZWxzW1tpXV0gPC0gYW5ub3QkZmluZVtvcmRlcihtYXRjaChhbm5vdCRicm9hZCwgYnJvYWRfb3JkZXIpKV0KICAKICAjIGNyZWF0ZSBpbmRleCBmb3IgY29sb3IgY29kaW5nCiAgY29sX3RhYmxlW1tpXV0gPC0gYW5ub3QgJT4lCiAgICBsZWZ0X2pvaW4oY2x1c3RfY29sLCBieSA9ICJicm9hZCIpICU+JSAKICAgIHNlbGVjdChjKCJmaW5lIiwgImNvbG9yIikpCiAgCiAgIyBhZGQgY2x1c3RlciBhbm5vdGF0aW9uIHRvIG1ldGEgZGF0YQogIG15LnNlQG1ldGEuZGF0YSA8LSBteS5zZUBtZXRhLmRhdGEgJT4lIAogICAgcm93bmFtZXNfdG9fY29sdW1uKCJyb3duYW1lIikgJT4lIAogICAgbGVmdF9qb2luKGFubm90LCBieSA9ICJzZXVyYXRfY2x1c3RlcnMiKSAlPiUgCiAgICBtdXRhdGUoZmluZSA9IGZhY3RvcihmaW5lLCBsZXZlbHMgPSBhbm5vdCRmaW5lKSkgJT4lIAogICAgY29sdW1uX3RvX3Jvd25hbWVzKCJyb3duYW1lIikKICAKICBteS5zZXNbW2ldXSA8LSBteS5zZQoKfQoKbmFtZXMobXkuc2VzKSA8LSBjKCJHZ19EMDVfY3RybCIsICJHZ19EMDdfY3RybCIsICJHZ19EMTBfY3RybCIpCm5hbWVzKGNvbF90YWJsZSkgPC0gYygiR2dfRDA1X2N0cmwiLCAiR2dfRDA3X2N0cmwiLCAiR2dfRDEwX2N0cmwiKQpuYW1lcyhvcmRfbGV2ZWxzKSA8LSBjKCJHZ19EMDVfY3RybCIsICJHZ19EMDdfY3RybCIsICJHZ19EMTBfY3RybCIpCgpybShteS5zZSwgYW5ub3QpCmBgYAoKYGBge3IgaW5wdXQgZGF0YSwgZWNobz1UUlVFfQojIFRoZSByZWZlcmVuY2UgV0dDTkEgZGF0YS4gV2UgY2FuIGhhdmUgc2V2ZXJhbCwgaWYgd2Ugd2FudCB0byB0ZXN0IG1hbnkgYXQgdGhlIHNhbWUgdGltZQpXR0NOQV9kYXRhID0gbGlzdCgpCldHQ05BX2RhdGFbWzFdXSA9IHJlYWRSRFMoIn4vc3BpbmFsX2NvcmRfcGFwZXIvb3V0cHV0L0dnX2RldmVsX2ludF9zY1dHQ05BXzI1MDcyMy5yZHMiKQpteS53c3ViID1saXN0KCkKbXkud3N1YltbMV1dPSBjKDE6MjIpCgojIHRoZSBuYW1lIG9mIGVhY2ggc2FtcGxlLCBhcyB0aGV5IGFwcGVhciBpbiBteS5maWxlcyBhbmQgaW4gdGhlIG1ldGFkYXRhIG9mIHRoZSBjb21iaW5lZCBvYmplY3QKbXkuc2FtcGxlbmFtZXMgPSBjKCJHZ19EMDVfY3RybCIsICJHZ19EMDdfY3RybCIsICJHZ19EMTBfY3RybCIpCgojIFRoZSBzdWJzZXQgb2YgY2x1c3RlcnMgaW4gZWFjaCBvZiB0aGUgY29ycmVzcG9uZGluZyBzYW1wbGVzCm15LnN1YnNldD1saXN0KGMoMToyNCksCiAgICAgICAgICAgICAgIGMoMToyNiksCiAgICAgICAgICAgICAgIGMoMToyMikpCgojIFRoaXMgaXMganVzdCB0byBhZGQgYSBsaXR0bGUgYml0IG1vcmUgc2Vuc2UgdG8gdGhlIG1vZHVsZXMsIHNvIHRoYXQgd2UgZG9uJ3QgZ2V0IGp1c3QgYSBjb2xvci4gQ29ycmVzcG9uZHMgdG8gV0dDTkFfZGF0YQpteS5tb2R1bGVuYW1lcyA9IGxpc3QoKQpteS5tb2R1bGVuYW1lc1tbMV1dID0gYygxOjIyKQoKYGBgCgpgYGB7ciBwcmUtcHJvY2VzcywgZWNobz1UUlVFfQoKI1N1YnNldCB0aGUgc2V1cmF0IG9iamVjdHMgaWYgbmVlZGVkIGFzIGRlZmluZWQgYWJvdmUKZm9yIChpIGluIDE6bGVuZ3RoKG15LnNlcykpIHsKICBteS5zZXNbW2ldXSA9IHN1YnNldChteS5zZXNbW2ldXSwgaWRlbnRzID0gbXkuc3Vic2V0W1tpXV0pCn0KCiMgVGFrZSBvbmx5IGdlbmVzIHRoYXQgYXJlIHByZXNlbnQgaW4gdGhlIGFsbCBzYW1wbGVzCm15LmRjcyA9IGxpc3QoKQoKIyBXZSBnbyB0cmhvdWdoIGVhY2ggV0dDTkEgZGF0YQpmb3IoaSBpbiAxOmxlbmd0aChXR0NOQV9kYXRhKSkgewogICNXZSBzdGFydCB3aXRoIGNvbXBsZXRlIG1vZHVsZXMKICBteS5kYyA9IFdHQ05BX2RhdGFbW2ldXSRkeW5hbWljQ29scwogIAogICNSZW1vdmUgdGhlIGZldyBnZW5lcyB0aGF0IGFyZSBub3QgZm91bmQgaW4gdGhlIG90aGVyIGRhdGFzZXRzCiAgbXkuZGMgPSBteS5kY1t3aGljaChuYW1lcyhteS5kYykgJWluJSByb3duYW1lcyhteS5zZXNbWzFdXUBhc3NheXMkUk5BQGRhdGEpKV0KICBteS5kYyA9IG15LmRjW3doaWNoKG5hbWVzKG15LmRjKSAlaW4lIHJvd25hbWVzKG15LnNlc1tbMl1dQGFzc2F5cyRSTkFAZGF0YSkpXQogIG15LmRjID0gbXkuZGNbd2hpY2gobmFtZXMobXkuZGMpICVpbiUgcm93bmFtZXMobXkuc2VzW1szXV1AYXNzYXlzJFJOQUBkYXRhKSldCiAgCiAgbXkuZGMgPSBteS5kY1t3aGljaChteS5kYyAlaW4lIG5hbWVzKHRhYmxlKFdHQ05BX2RhdGFbW2ldXSRkeW5hbWljQ29scykpW215LndzdWJbW2ldXV0pXQogIAogIG15LmRjc1tbaV1dID0gbXkuZGMKICAKfQpgYGAKCiMgTW9kdWxlIGdlbmUgY29ycmVsYXRpb24KCkhlcmUsIHdlIGRvIGEgY29ycmVsYXRpb24gbWF0cml4IC8gaGVhdG1hcCwgdG8gc2VlIHdoaWNoIGNlbGwgY2x1c3RlcnMgZ3JvdXAgdG9naGV0ZXIuIFRoaXMgaGVscHMgdXMgdG8gbWFrZSBtb3JlIGRldGFpbGVkIGRvdHBsb3RzLiAgClRoaXMgcGFydCBvZiB0aGUgc2NyaXB0IGNhbiBzdGlsbCBiZSB1c2VkIHRvIGNvbXBhcmUgc2V2ZXJhbCBXR0NOQSBkYXRhc2V0cyBpbiBwYXJhbGxlbC4gIAoKYGBge3J9CiMgYnJvYWQgY2x1c3RlciBjb2xvciB0YWJsZQphbGxfY29sIDwtIGRvLmNhbGwocmJpbmQsIGNvbF90YWJsZSkgJT4lIAogIHJvd25hbWVzX3RvX2NvbHVtbigic2FtcGxlIikgJT4lIAogIG11dGF0ZShzYW1wbGUgPSBzdWJzdHIoc2FtcGxlLCAxLCAxMSkpICU+JSAKICBtdXRhdGUoc2FtcGxlX2NlbGx0eXBlID0gcGFzdGUoc2FtcGxlLCBmaW5lLCBzZXAgPSAiXyIpKSAlPiUgCiAgc2VsZWN0KGMoImNvbG9yIiwgInNhbXBsZV9jZWxsdHlwZSIsICJzYW1wbGUiKSkKYGBgCgpgYGB7ciBtb2R1bGVfZXhwcmVzc2lvbl9kYXRhLCBlY2hvPVRSVUUsIGZpZy5oZWlnaHQ9MjAsIGZpZy53aWR0aD0yMH0KCiMgdGFrZSB0aGUgbm9ybWFsaXplZCBkYXRhLCBvZiBlYWNoIHNpbmdsZS1jZWxsIG9iamVjdC4KbXkuZGF0RXhwciA9IGxpc3QoKQoKIyBHbyB0cm91Z2ggZWFjaCBzZXVyYXQgb2JqZWN0CmZvciAoaSBpbiAxOmxlbmd0aChteS5zZXMpKSB7CiBteS5kYXRFeHByW1tpXV0gPSBkYXRhLmZyYW1lKG15LnNlc1tbaV1dQGFzc2F5cyRSTkFAZGF0YSkKfQoKIyBDYWxjdWxhdGUsIGZvciBldmVyeSBjZWxsLCBpbiBldmVyeSBzYW1wbGUsIHRoZSBhdmVyYWdlIGV4cHJlc3Npb24gb2YgZWFjaCBvZiB0aGUgbW9kdWxlcy4KbXkuTUVzID0gbGlzdCgpCgojIEZvciBlYWNoIHNldCBvZiBXR0NOQSBtb2R1bGVzCmZvciAoaSBpbiAxOmxlbmd0aChXR0NOQV9kYXRhKSkgewogIAogICMgTWFrZSBhIG5ldyBsaXN0CiAgbXkuTUVzW1tpXV0gPSBsaXN0KCkKICAKICAjIFBvcHVsYXRlIHdpdGggZGF0YSBmcmFtZXMgZnJvbSBlYWNoIHNldXJhdCBvYmplY3QKICBmb3IgKGogaW4gMTpsZW5ndGgobXkuc2VzKSkgewogICAgCiAgICAjIERhdGEgZnJhbWUgb2YgYXZlcmFnZSBtb2R1bGUgZXhwcmVzc2lvbiwgcGVyIGNlbGwuCiAgICBteS5NRXNbW2ldXVtbal1dID0gbW9kdWxlRWlnZW5nZW5lcyh0KG15LmRhdEV4cHJbW2pdXVtuYW1lcyhteS5kY3NbW2ldXSksXSksIGNvbG9ycyA9IG15LmRjc1tbaV1dKQogIH0KfQoKI0NhbGN1bGF0ZSBhdmVyYWdlIG9mIGV4cHJlc3Npb24sIHBlciBzYW1wbGUgYW5kIGNlbGwgY2x1c3RlcgpteS5tb2RhdmcgPSBsaXN0KCkKCmZvciAoaSBpbiAxOmxlbmd0aChXR0NOQV9kYXRhKSkgewogIAogIG15Lm1vZGF2Z1tbaV1dID0gbGlzdCgpCiAgCiAgZm9yIChqIGluIDE6bGVuZ3RoKG15LnNlcykpIHsKICAgIAogICAgI01ha2UgdGhlIG1lYW4gcGVyIGNlbGwgY2x1c3RlcnMKICAgIG15Lm1vZGF2Z1tbaV1dW1tqXV0gPSBhZ2dyZWdhdGUoCiAgICAgIG15Lk1Fc1tbaV1dW1tqXV0kYXZlcmFnZUV4cHIsCiAgICAgIGxpc3QobXkuc2VzW1tqXV1AbWV0YS5kYXRhJHNldXJhdF9jbHVzdGVycyksCiAgICAgIG1lYW4KICAgICAgKQogICAgIyBHaXZlIHRoZSBjZWxsIGNsdXN0ZXJzIG1lYW5pbmdmdWwgbmFtZXMKICAgIHJvd25hbWVzKG15Lm1vZGF2Z1tbaV1dW1tqXV0pID0gcGFzdGUwKAogICAgICBteS5zYW1wbGVuYW1lc1tqXSwKICAgICAgIl8iLAogICAgICBsZXZlbHMobXkuc2VzW1tqXV1AbWV0YS5kYXRhJGZpbmUpW215LnN1YnNldFtbal1dXQogICAgICApCiAgICB9Cn0KCgoKI0dhdGhlciBkYXRhIHRvIHBsb3QKCm15LndnbWF0ID0gbGlzdCgpCgpmb3IgKGkgaW4gMTpsZW5ndGgoV0dDTkFfZGF0YSkpIHsKICAKICAjYmluZCB0aGUgZXhwcmVzc2lvbiBkYXRhIGludG8gb25lIGRhdGFmcmFtZQogIG15LndnbWF0W1tpXV0gPSBkYXRhLnRhYmxlOjpyYmluZGxpc3QobXkubW9kYXZnW1tpXV0pCiAgI0dldCByaWQgb2YgdGhlIGV4dHJhIGNvbHVtbgogIG15LndnbWF0W1tpXV0gPSBkYXRhLmZyYW1lKG15LndnbWF0W1tpXV1bLC0xXSkKICAjUmVzdG9yZSB0aGUgcm93bmFtZXMKICByb3duYW1lcyhteS53Z21hdFtbaV1dKSA9IHVubGlzdChzYXBwbHkobXkubW9kYXZnW1tpXV0sIHJvd25hbWVzKSkKCn0KCiNHZXQgYSBkYXRhZnJhbWUgd2l0aCBhbm5vdGF0aW9ucyBmb3IgYWxsIHRoZSBzYW1wbGVzIGFuZCBjb2xvcnMgd2UgbmVlZC4KbXkubWV0YW0gPC0gbGlzdCgpCgpmb3IgKGkgaW4gc2VxKG15LnNlcykpIHsKICBteS5tZXRhbVtbaV1dIDwtIG15LnNlc1tbaV1dW1tdXQogIHJvd25hbWVzKG15Lm1ldGFtW1tpXV0pIDwtIHBhc3RlMChyb3duYW1lcyhteS5tZXRhbVtbaV1dKSwgIl8iLCBpKQp9Cm15Lm1ldGFtIDwtIGRvLmNhbGwocmJpbmQsIG15Lm1ldGFtKQoKbXkubWV0YW0kb3JpZy5pZGVudCA8LSBzdHJfcmVwbGFjZV9hbGwobXkubWV0YW0kb3JpZy5pZGVudCwgcGF0dGVybiA9ICAiR2dfY3RybF8xIiwgIkdnX0QxMF9jdHJsIikKCiMgbXkubWV0YW0kc2FtcGxlX2NlbGx0eXBlID0gcGFzdGUwKHN1YnN0cihteS5tZXRhbSRvcmlnLmlkZW50LDcsOSksIl8iLG15Lm1ldGFtJHNldXJhdF9jbHVzdGVycykKbXkubWV0YW0kc2FtcGxlX2NlbGx0eXBlID0gcGFzdGUwKG15Lm1ldGFtJG9yaWcuaWRlbnQsICJfIiwgbXkubWV0YW0kZmluZSkKCm15Lm1ldGFtIDwtIG15Lm1ldGFtICU+JSAKICB0aWJibGU6OnJvd25hbWVzX3RvX2NvbHVtbih2YXIgPSAiY2VsbF9JRCIpICU+JQogIGRwbHlyOjpsZWZ0X2pvaW4oYWxsX2NvbCwgYnkgPSAic2FtcGxlX2NlbGx0eXBlIikgJT4lCiAgdGliYmxlOjpjb2x1bW5fdG9fcm93bmFtZXModmFyID0gImNlbGxfSUQiKQoKCiMgZ2V0IHNhbXBsZSBjb2xvcnMKbXkuY29sc20gPSBjKCJncmV5IiwgImdyZXkzMCIsICJibGFjayIpCm5hbWVzKG15LmNvbHNtKSA8LSBjKCJHZ19EMDVfY3RybCIsICJHZ19EMDdfY3RybCIsICJHZ19EMTBfY3RybCIpCiAKI1RoZSBjb2xvcnMgZm9yIHRoZSBzYW1wbGVzIGFuZCBjbHVzdGVycy4gRmlyc3QgdGhlIGNsb3Nlc3QgdG8gdGhlIGhlYXRtYXAuIEFkZCBhIHdoaXRlIHNwYWNlIHRvIGVhc3kgdGhlIGV5ZSBhbmQgbWFrZSBsZXNzIGNvbmZ1c3NpbmcKbXkuaGVhdGNvbHMgPWxpc3QoKQpmb3IgKGkgaW4gMTpsZW5ndGgobXkud2dtYXQpKSB7CiAgbXkuaGVhdGNvbHNbW2ldXSA9IGFzLm1hdHJpeChkYXRhLmZyYW1lKAogICAgY2x1c3Rlcj0gYXMuY2hhcmFjdGVyKGFsbF9jb2wkY29sb3JbbWF0Y2gocm93bmFtZXMobXkud2dtYXRbW2ldXSksIGFsbF9jb2wkc2FtcGxlX2NlbGx0eXBlKV0pLAogICAgIi4iID0gIndoaXRlIiwKICAgIHNhbXBsZT0gYXMuY2hhcmFjdGVyKG15LmNvbHNtW21hdGNoKGFsbF9jb2wkc2FtcGxlLCBuYW1lcyhteS5jb2xzbSkpXSkpCiAgICApCn0KCnJtKG15LmRhdEV4cHIsIG15Lk1FcywgbXkuZGNzKQpgYGAKCiMjIHNwZWFybWFuIGNvcnJlbGF0aW9uIGhlYXRtYXAKCiMjIyBhbm5vdGF0aW9ucwoKYGBge3IgYW5ub3QtbGlzdH0KCiMgbmFtZXMgYW5kIGNvbG9ycyBmb3IgdGhlIGhlYXRtYXAgYW5ub3RhdGlvbgphbm5vdF9uYW1lIDwtIGRhdGEuZnJhbWUoCiAgIkNlbGx0eXBlcyIgPSBhbGxfY29sJHNhbXBsZV9jZWxsdHlwZSwKICAiU2FtcGxlIiAgICA9IGFsbF9jb2wkc2FtcGxlLAogIHJvdy5uYW1lcyA9IGFsbF9jb2wkc2FtcGxlX2NlbGx0eXBlKQogIAoKcGhlYXRfY29sX3RhYmxlIDwtIGRvLmNhbGwocmJpbmQsIGNvbF90YWJsZSkgJT4lIAogIHJvd25hbWVzX3RvX2NvbHVtbigic2FtcGxlIikgJT4lIAogIG11dGF0ZShzYW1wbGUgPSBzdWJzdHIoc2FtcGxlLCAxLDExKSkgJT4lIAogIG11dGF0ZShmaW5lID0gcGFzdGUoc2FtcGxlLCBmaW5lLCBzZXAgPSAiXyIpKQoKIyBtYXRjaCBjb2xvciB0YWJsZSB3aXRoIGFubm90YXRpb24KcGhlYXRfY29sX3RhYmxlIDwtIHBoZWF0X2NvbF90YWJsZVttYXRjaChhbm5vdF9uYW1lJENlbGx0eXBlcywgcGhlYXRfY29sX3RhYmxlJGZpbmUpLF0KCmFubm90X2NvbCA8LSBsaXN0KAogIENlbGx0eXBlcyA9IHBoZWF0X2NvbF90YWJsZSRjb2xvciwKICBTYW1wbGUgPSBjKEdnX0QwNV9jdHJsID0gIiNBNEE0QTQiLAogICAgICAgICAgICAgR2dfRDA3X2N0cmwgPSAiIzUxNTE1MSIsCiAgICAgICAgICAgICBHZ19EMTBfY3RybCA9ICIjMDAwMDAwIikKICApCgpuYW1lcyhhbm5vdF9jb2xbWzFdXSkgPC0gYW5ub3RfbmFtZSRDZWxsdHlwZXMKCmBgYAoKYGBge3IsICBmaWcuaGVpZ2h0PTEwLCBmaWcud2lkdGg9MTN9CmhlYXRfY29sIDwtIGNvbG9yUmFtcFBhbGV0dGUoY29sb3JzID0gYygiZG9kZ2VyYmx1ZTQiLCJkb2RnZXJibHVlIiwgIndoaXRlIiwgInJlZCIsICJkYXJrcmVkIikpCgp0b3Bsb3QgPC0gY29yKHQobXkud2dtYXRbWzFdXSksIG1ldGhvZCA9ICJzcGVhcm1hbiIpCgojQ2FsY3VsYXRlIHRoZSBkaXN0YW5jZSAxLWNvciwgYW5kIHRoZW4gY2FsY3VsYXRlIGRlbmRvZ3JhbXMgdG8gY2x1c3RlciB0aGUgY2x1c3RlcnMKbXkuaGNsdXN0ID0gaGNsdXN0KGFzLmRpc3QoMS1jb3IodChteS53Z21hdFtbMV1dKSwgbWV0aG9kID0gInNwZWFybWFuIikpKQoKIyBsb3dlciBsaW1pdCB0byBzY2FsZSBjb2xvciBiYXIKbG93X2xpbWl0IDwtIDEwMCAtIGFicyhyb3VuZChyYW5nZSh0b3Bsb3QpWzFdKjEwMCkpCgpwaGVhdG1hcCh0b3Bsb3QscmV2QyA9IFQsCiAgICAgICAgICAgICAgICAgbWFpbiA9ICJzcGVhcm1hbiBjb3JyZWxhdGlvbiBvZiBhdmVyYWdlIG1vZHVsZSBlaWdlbmdlbmUgZXhwcmVzc2lvbiBieSBjbHVzdGVyXG4gRGVuZHJvZ3JhbSBiYXNlZCBvbiAnZGlzdGFuY2UgMS1jb3InIiwKICAgICAgICAgICAgICAgICBmb250c2l6ZSA9IDgsICAKICAgICAgICAgICAgICAgICBjb2xvciA9IGhlYXRfY29sKDIwMClbbG93X2xpbWl0OjIwMF0sCiAgICAgICAgICAgICAgICAgc2hvd19jb2xuYW1lcyA9IEYsCiAgICAgICAgICAgICAgICAgY2x1c3Rlcl9yb3dzID0gbXkuaGNsdXN0LAogICAgICAgICAgICAgICAgIGNsdXN0ZXJfY29scyA9IG15LmhjbHVzdCwKICAgICAgICAgICAgICAgICB0cmVlaGVpZ2h0X3JvdyA9IDAsIAogICAgICAgICAgICAgICAgIGFubm90YXRpb25fY29sID0gYW5ub3RfbmFtZSwKICAgICAgICAgICAgICAgICBhbm5vdGF0aW9uX2NvbG9ycyA9IGFubm90X2NvbCwKICAgICAgICAgICAgICAgICBhbm5vdGF0aW9uX2xlZ2VuZCA9IEYsCiAgICAgICAgICAgICAgICAgYm9yZGVyX2NvbG9yID0gTkEKICAgICAgICAgICAgICAgICApCgpjb2xiYXIgPC0gYyhtaW4oY29yKHQobXkud2dtYXRbWzFdXSksIG1ldGhvZCA9ICJzcGVhcm1hbiIpKSwKICAgICAgICAgICAgbWF4KGNvcih0KG15LndnbWF0W1sxXV0pLCBtZXRob2QgPSAic3BlYXJtYW4iKSkpCgojQ2FsY3VsYXRlIHRoZSBkaXN0YW5jZSAxLWNvciwgYW5kIHRoZW4gY2FsY3VsYXRlIGRlbmRvZ3JhbXMgdG8gY2x1c3RlciB0aGUgY2x1c3RlcnMKbXkuaGNsdXN0ID0gYXMuZGVuZHJvZ3JhbShoY2x1c3QoYXMuZGlzdCgxLWNvcih0KG15LndnbWF0W1sxXV0pLCBtZXRob2QgPSAic3BlYXJtYW4iKSkpKQoKI1Bsb3QhCnBkZigifi9zcGluYWxfY29yZF9wYXBlci9maWd1cmVzL0ZpZ18xX21vZHVsZV9nZW5lX2hlYXRtYXBfc3BlYXJtYW4ucGRmIiwgd2lkdGggPSAxMSwgaGVpZ2h0ID0gMTEpCmhlYXRtYXAuNChjb3IodChteS53Z21hdFtbMV1dKSwgbWV0aG9kID0gInNwZWFybWFuIiksCiAgICAgICAgICBjb2wgPSBjb2xvclJhbXBQYWxldHRlKGMoImRvZGdlcmJsdWU0IiwiZG9kZ2VyYmx1ZSIsICJ3aGl0ZSIsICJyZWQiLCAiZGFya3JlZCIpKShuID0gMTAwMCksCiAgICAgICAgICB0cmFjZT0ibm9uZSIsCiAgICAgICAgICBsaGVpPWMoMC4yLDEuNiksCiAgICAgICAgICBsd2lkPWMoMC4yLDEuNiksCiAgICAgICAgICBzY2FsZSA9ICJub25lIixtYXJnaW5zID0gYygxMCwxMCksCiAgICAgICAgICBjZXhDb2wgPSAwLjc1LAogICAgICAgICAgcmV2QyA9IFRSVUUsCiAgICAgICAgICBDb2xTaWRlQ29sb3JzID0gbXkuaGVhdGNvbHNbWzFdXSwKICAgICAgICAgIFJvd1NpZGVDb2xvcnMgPSB0KG15LmhlYXRjb2xzW1sxXV1bLDM6MV0pLFJvd3YgPSBteS5oY2x1c3QsIENvbHYgPSBteS5oY2x1c3QpCgoKcGRmKCJ+L3NwaW5hbF9jb3JkX3BhcGVyL2ZpZ3VyZXMvRmlnXzFfbW9kdWxlX2dlbmVfaGVhdG1hcF9jb2xvcl9rZXlfc3BlYXJtYW4ucGRmIiwgd2lkdGggPSAxMCwgaGVpZ2h0ID0gMTApCiMgaGVhdG1hcCBzaW5jZSBjb2xvciBrZXkgaXMgbWlzc2luZyBpbiBmaXJzdCBoZWF0bWFwCmhlYXRtYXAuNChyYmluZChjb2xiYXIsIGNvbGJhciksCiAgICAgICAgICBjb2wgPSBjb2xvclJhbXBQYWxldHRlKGMoImRvZGdlcmJsdWU0IiwiZG9kZ2VyYmx1ZSIsICJ3aGl0ZSIsICJyZWQiLCAiZGFya3JlZCIpKShuID0gMTAwMCksCiAgICAgICAgICB0cmFjZT0ibm9uZSIsCiAgICAgICAgICBsaGVpPWMoMC4yLDEuNiksCiAgICAgICAgICBsd2lkPWMoMC4yLDEuNiksCiAgICAgICAgICBzY2FsZSA9ICJub25lIixtYXJnaW5zID0gYygxMCwxMCksCiAgICAgICAgICBjZXhDb2wgPSAwLjc1KQoKYGBgCgojIyBoZWF0bWFwIG9mIG1vZHVsZSBwc2V1ZG9idWxrIGF2ZXJhZ2UgZXhwcmVzc2lvbgoKYGBge3IsICBmaWcuaGVpZ2h0PTE1LCBmaWcud2lkdGg9MTB9CiMgbW9kdWxlIGNvbG9ycwpteS5jb2xjb2xzID0gbGlzdCgpCgpmb3IgKGkgaW4gMTpsZW5ndGgobXkud2dtYXQpKSB7CiAgICBteS5jb2xjb2xzW1tpXV0gPSBhcy5tYXRyaXgobmFtZXModGFibGUoV0dDTkFfZGF0YVtbaV1dJGR5bmFtaWNDb2xzKSkpCn0KCmF2Zy5tb2QuZWlnZW5nZW5lcyA8LSBXR0NOQV9kYXRhW1sxXV0kc2MuTUVMaXN0JGF2ZXJhZ2VFeHByCnJtKFdHQ05BX2RhdGEpCgpodG1wIDwtIGhlYXRtYXAuNChhcy5tYXRyaXgobXkud2dtYXRbWzFdXSksCiAgICAgICAgICBjb2wgPSBjb2xvclJhbXBQYWxldHRlKGMoImRvZGdlcmJsdWU0IiwiZG9kZ2VyYmx1ZSIsICJ3aGl0ZSIsICJyZWQiLCAiZGFya3JlZCIpKShuID0gMTAwMCksCiAgICAgICAgICB0cmFjZT0ibm9uZSIsCiAgICAgICAgICBzY2FsZSA9ICJyb3ciLAogICAgICAgICAgbWFyZ2lucyA9IGMoMTAsMTApLAogICAgICAgICAgQ29sU2lkZUNvbG9ycyA9IG15LmNvbGNvbHNbWzFdXSwgCiAgICAgICAgICBSb3dTaWRlQ29sb3JzID0gdChteS5oZWF0Y29sc1tbMV1dKSkKCm1vZHVsZV9vcmRlciA8LSBodG1wW1siY29sSW5kIl1dCgpwZGYoIn4vc3BpbmFsX2NvcmRfcGFwZXIvZmlndXJlcy9EZXZlbF9tb2R1bGVfdl9jbHVzdGVyc19oZWF0bWFwLnBkZiIsIHdpZHRoID0gNywgaGVpZ2h0ID0gMTApCmhlYXRtYXAuNChhcy5tYXRyaXgobXkud2dtYXRbWzFdXSksCiAgICAgICAgICBjb2wgPSBjb2xvclJhbXBQYWxldHRlKGMoImRvZGdlcmJsdWU0IiwiZG9kZ2VyYmx1ZSIsICJ3aGl0ZSIsICJyZWQiLCAiZGFya3JlZCIpKShuID0gMTAwMCksCiAgICAgICAgICB0cmFjZT0ibm9uZSIsCiAgICAgICAgICBzY2FsZSA9ICJyb3ciLAogICAgICAgICAgbWFyZ2lucyA9IGMoMTAsMTApLAogICAgICAgICAgQ29sU2lkZUNvbG9ycyA9IG15LmNvbGNvbHNbWzFdXSwgCiAgICAgICAgICBSb3dTaWRlQ29sb3JzID0gdChteS5oZWF0Y29sc1tbMV1dKSkKCmBgYAoKCgojIExvYWQgdGhlIGludGVncmF0ZWQgZGF0YSBzZXQKClRoZSBpbnRlZ3JhdGVkIGRhdGEgc2V0IG9uIHdoaWNoIHRoZSBXR0NOQSBpcyBjYWxjdWxhdGVkLiBXZSBwbG90IGl0LCBzcGxpdCBieSB0aGUgb3JpZ2luYWwgY2VsbCB0eXBlcyBmcm9tIHRoZSB0aHJlZSBvcmlnaW5hbCBzYW1wbGVzLiAKCmBgYHtyIGRpbXBsb3RzLCBmaWcud2lkdGg9MTB9CiMgVGhpcyBpcyBhIGZpbGUgb2YgYWxsIHRoZSBjb21iaW5lZCBtb3VzZSBkYXRhc2V0cywgbm9ybWFsaXplZCBhbmQgc3VjaC4KbXkuc2VjID0gcmVhZFJEUygifi9zcGluYWxfY29yZF9wYXBlci9kYXRhL0dnX2RldmVsX2ludF9zZXVyYXRfMjUwNzIzLnJkcyIpCgppZGVudGljYWwocm93bmFtZXMobXkubWV0YW0pLCBjb2xuYW1lcyhteS5zZWMpKQoKI1doaWNoIFdHQ05BIGRhdGFzZXQgYXJlIHdlIHVzaW5nPwpteS5XID0gMQoKI0Nob29zZSBob3cgbWFueSBncm91cHMgb2YgY2VsbCBjbHVzdGVycyB3ZSB3YW50IHRvIHVzZS4gQmFzZWQgb24gdGhlIGRpc3RhbmNlIGNsdXN0ZXJpbmcgZnJvbSBhYm92ZQpteS5jbGNsID0gY3V0cmVlKGhjbHVzdChhcy5kaXN0KDEtY29yKHQobXkud2dtYXRbW215LlddXSkpKSksIGsgPSA1KQoKbXkubWV0YW0kc2FtcGxlX2NlbGx0eXBlIDwtIGZhY3RvcihteS5tZXRhbSRzYW1wbGVfY2VsbHR5cGUsIGxldmVscyA9IG5hbWVzKG15LmNsY2wpKQojU2V0IHRoZSBpZGVudGl0aWVzIG9mIHRoZSBpbnRlZ3JhdGVkIGRhdGEsIHRvIHRoZSBhbm5vdGF0ZWQgY2x1c3RlcnMKbXkuc2VjID0gU2V0SWRlbnQobXkuc2VjLCB2YWx1ZSA9IG15Lm1ldGFtJHNhbXBsZV9jZWxsdHlwZSkKCnAxIDwtIERpbVBsb3QoCiAgbXkuc2VjLAogIHJlZHVjdGlvbiA9ICJ0c25lIiwKICBsYWJlbCA9IFRSVUUsCiAgcmVwZWwgPSBUUlVFLAogIGNvbHMgPSBteS5oZWF0Y29sc1tbMV1dWywiY2x1c3RlciJdLAogIHNwbGl0LmJ5ID0gIm9yaWcuaWRlbnQiCiAgKSArIAogIE5vTGVnZW5kKCkKCnAxCgpwZGYoIn4vc3BpbmFsX2NvcmRfcGFwZXIvZmlndXJlcy9EZXZlbF9zcGxpdF90c25lLnBkZiIsIGhlaWdodCA9IDcsIHdpZHRoID0gMTUpCiNQbG90IHNwbGl0IHRzbmUKcDEKYGBgCgojIEF2Zy4gbW9kdWxlIGV4cC4gYnkgc3RhZ2UKCnRTTkUgRGltUGxvdHMgc2hvd2luZyB0aGUgYXZlcmFnZSBleHByZXNzaW9uIG9mIGVhY2ggbW9kdWxlIGJ5IHN0YWdlLgoKYGBge3IgQUUsIGZpZy53aWR0aD0xMiwgZmlnLmhlaWdodD00MH0KCmZvciAoaSBpbiBzZXEobXkuc2VzKSkgewogICMgcHJlcGFyZSBhdmVyYWdlIGV4cHJlc3Npb24gdGFibGUKICB0bXAgPC0gYXZnLm1vZC5laWdlbmdlbmVzICU+JQogICAgdGliYmxlOjpyb3duYW1lc190b19jb2x1bW4oImNlbGxfSUQiKSAlPiUKICAgIGRwbHlyOjpmaWx0ZXIoZ3JlcGwocGFzdGUwKCJfIiwgaSwgIiQiKSwgY2VsbF9JRCkpICU+JQogICAgZHBseXI6Om11dGF0ZShjZWxsX0lEID0gc3RyaW5ncjo6c3RyX3JlbW92ZV9hbGwoY2VsbF9JRCwgcGFzdGUwKCJfIiwgaSkpKSAlPiUKICAgIHRpYmJsZTo6Y29sdW1uX3RvX3Jvd25hbWVzKCJjZWxsX0lEIikKICAKICBpZGVudGljYWwocm93bmFtZXModG1wKSwgY29sbmFtZXMobXkuc2VzW1tpXV0pKQogICMgYWRkIG1ldGEgZGF0YSB0byB0aGUgc2V1cmF0IG9iamVjdHMKICBteS5zZXNbW2ldXSA8LSBBZGRNZXRhRGF0YShteS5zZXNbW2ldXSwgdG1wKQp9CgojbWF4IGFuZCBtaW4gZXhwcmVzc2lvbiBwZXIgbW9kdWxlIChjb2x1bW4gbWF4KQptb2RfbWF4IDwtIGFwcGx5KGF2Zy5tb2QuZWlnZW5nZW5lcywgTUFSR0lOID0gMiwgRlVOID0gbWF4KVttb2R1bGVfb3JkZXJdCm1vZF9taW4gPC0gYXBwbHkoYXZnLm1vZC5laWdlbmdlbmVzLCBNQVJHSU4gPSAyLCBGVU4gPSBtaW4pW21vZHVsZV9vcmRlcl0KCm1vZHBsb3RzIDwtIGxpc3QoKQptb2RwbG90c1tbMV1dIDwtIGxpc3QoKQptb2RwbG90c1tbMl1dIDwtIGxpc3QoKQptb2RwbG90c1tbM11dIDwtIGxpc3QoKQoKbW9kdWxlc19pbl9vcmRlciA8LSBjb2xuYW1lcyh0bXApW21vZHVsZV9vcmRlcl0KCiMgcGxvdCB0aGUgbW9kdWxlcyBzcGxpdCB0byB0aGUgc3RhZ2VzCmZvciAoaSBpbiBzZXEobXkuc2VzKSkgewogIGZvciAoaiBpbiBzZXEobmNvbCh0bXApKSkgewogIAogICAgbW9kcGxvdHNbW2ldXVtbal1dICA8LSBGZWF0dXJlUGxvdCgKICAgICAgbXkuc2VzW1tpXV0sCiAgICAgIGZlYXR1cmVzID0gbW9kdWxlc19pbl9vcmRlcltqXSwKICAgICAgcmVkdWN0aW9uID0gInRzbmUiLAogICAgICBjb2xzID0gYygiZ3JleTkwIiwgc3Vic3RyaW5nKG1vZHVsZXNfaW5fb3JkZXJbal0sIDMpKQogICAgICApICsKICAgICAgZ2d0aXRsZShzdHJpbmdyOjpzdHJfcmVtb3ZlKG1vZHVsZXNfaW5fb3JkZXJbal0sIl5BRSIpKSArCiAgICAgIHNjYWxlX2NvbG9yX2dyYWRpZW50KGxvdz0iaXZvcnkyIiwgaGlnaD1zdWJzdHJpbmcobW9kdWxlc19pbl9vcmRlcltqXSwgMyksICNjb2xvcnMgaW4gdGhlIHNjYWxlCiAgICAgICAgICAgICAgICAgIyBicmVha3M9c2VxKG1vZF9taW5bal0sIG1vZF9tYXhbal0sIDAuMSksICNicmVha3MgaW4gdGhlIHNjYWxlIGJhcgogICAgICAgICAgICAgICAgIGxpbWl0cz1jKG1vZF9taW5bal0sIG1vZF9tYXhbal0pKSAjc2FtZSBsaW1pdHMgZm9yIHBsb3RzCgogICAgCiAgICB9Cn0KCnRtcCA8LSBjKG1vZHBsb3RzW1sxXV0sIG1vZHBsb3RzW1syXV0sIG1vZHBsb3RzW1szXV0pCgpncmlkRXh0cmE6OmdyaWQuYXJyYW5nZShncm9icyA9IHRtcCwgbmNvbCA9IDMsIGFzLnRhYmxlID0gRkFMU0UpCgpwZGYoIn4vc3BpbmFsX2NvcmRfcGFwZXIvZmlndXJlcy9EZXZlbF9tb2R1bGVzX0FFX3Bsb3RzLnBkZiIsIHdpZHRoID0gMTIsIGhlaWdodCA9IDcwKQpncmlkRXh0cmE6OmdyaWQuYXJyYW5nZShncm9icyA9IHRtcCwgbmNvbCA9IDMsIGFzLnRhYmxlID0gRkFMU0UpCgpwZGYoIn4vc3BpbmFsX2NvcmRfcGFwZXIvZmlndXJlcy9GaWdfMV9NTl9tb2RfdHNuZS5wZGYiLCBoZWlnaHQgPSA2LCB3aWR0aCA9IDEyKQptb2RwbG90c1tbMV1dW1syMl1dICsgbW9kcGxvdHNbWzFdXVtbNl1dIHwKICBtb2RwbG90c1tbMl1dW1syMl1dICsgbW9kcGxvdHNbWzJdXVtbNl1dIHwKICBtb2RwbG90c1tbM11dW1syMl1dICsgbW9kcGxvdHNbWzNdXVtbNl1dCgpgYGAKCiMgQUUgb3ZlciB0aW1lCgpXZSBwbG90IHRoZSBhdmVyYWdlIGV4cHJlc3Npb24gb2YgZWFjaCBtb2R1bGUgaW4gdGhlIHRocmVlIHN0YWdlcyBhbmQgdGhlIDUgYnJvYWQgY2VsbCB0eXBlIGNsdXN0ZXJzIHByZXNlbnQgaW4gYWxsIDMgc3RhZ2VzLgoKYGBge3IgQUUtb3Zlci10aW1lLCBmaWcuaGVpZ2h0PTcsIGZpZy53aWR0aD0xMn0KIyBtb2R1bGUgYW5ub3RhdGlvbnMKbW9kX2Fubm90IDwtIHJlYWQuY3N2KCJ+L3NwaW5hbF9jb3JkX3BhcGVyL2Fubm90YXRpb25zL0dnX2RldmVsX2ludF9zY1dHQ05BX21vZHVsZV9hbm5vdGF0aW9uLmNzdiIpICU+JQogIGRwbHlyOjptdXRhdGUobW9kdWxlID0gc3RyX3JlcGxhY2VfYWxsKG1vZHVsZSwgIlxcZHsxLDJ9XFxfIiwgIkFFIikpCgptZXRhIDwtIGxpc3QoKQoKZm9yIChpIGluIHNlcShteS5zZXMpKSB7CiAgbWV0YVtbaV1dIDwtIG15LnNlc1tbaV1dQG1ldGEuZGF0YSAlPiUKICAgIHRpYmJsZTo6cm93bmFtZXNfdG9fY29sdW1uKCJjZWxsX0lEIikgJT4lCiAgICBkcGx5cjo6bXV0YXRlKGNlbGxfSUQgPSBwYXN0ZTAoY2VsbF9JRCwgIl8iLCBpKSkgJT4lCiAgICBkcGx5cjo6c2VsZWN0KGMoImNlbGxfSUQiLCAiYnJvYWQiKSkKfQoKbWV0YSA8LSBkby5jYWxsKHJiaW5kLCBtZXRhKQoKIyBtZWFuIGF2ZXJhZ2UgZXhwcmVzc2lvbiBieSBzdGFnZQptZWFuX0FFIDwtIGF2Zy5tb2QuZWlnZW5nZW5lcyAlPiUKICB0aWJibGU6OnJvd25hbWVzX3RvX2NvbHVtbigiY2VsbF9JRCIpICU+JQogIGRwbHlyOjptdXRhdGUoc3RhZ2UgPSBzdHJpbmdyOjpzdHJfc3ViKGNlbGxfSUQsIC0xKSkgJT4lCiAgZHBseXI6Om11dGF0ZShzdGFnZSA9IGZhY3RvcihzdGFnZSwgbGV2ZWxzID0gYygxOjMpLCBsYWJlbHMgPSBjKCJEMDUiLCAiRDA3IiwgIkQxMCIpKSkgJT4lCiAgZHBseXI6OmxlZnRfam9pbihtZXRhLCBieSA9ICJjZWxsX0lEIikgJT4lCiAgdGliYmxlOjpjb2x1bW5fdG9fcm93bmFtZXMoImNlbGxfSUQiKSAlPiUKICB0aWR5cjo6dW5pdGUoInN0YWdlX2NsIiwgc3RhZ2UsIGJyb2FkLCBzZXAgPSAiXyIpICU+JQogIGRwbHlyOjpncm91cF9ieShzdGFnZV9jbCkgJT4lCiAgZHBseXI6OnN1bW1hcmlzZV9lYWNoKG1lYW4pICU+JQogIGRwbHlyOjp1bmdyb3VwKCkgJT4lCiAgZ2F0aGVyKGtleT0ibW9kdWxlIiwgdmFsdWUgPSAiQUUiLCAtc3RhZ2VfY2wpICU+JQogIGRwbHlyOjpsZWZ0X2pvaW4obW9kX2Fubm90WywgYygxLDMpXSwgYnkgPSAibW9kdWxlIikgJT4lCiAgdGlkeXI6OnNlcGFyYXRlKCJzdGFnZV9jbCIsIGMoInN0YWdlIiwgImJyb2FkIiksIHNlcCA9ICJfIiwgcmVtb3ZlID0gRkFMU0UpIAojICU+JQogICMgZHBseXI6OmZpbHRlcihicm9hZCAlaW4lIGMoInByb2dlbml0b3JzIiwgIm5ldXJvbnMiLCAiUlAiLCAiRlAiLCAicGVyaWN5dGVzIikpCgpsYWJlbHNfZG90cGxvdCA8LSBzdHJpbmdyOjpzdHJfcmVtb3ZlKG1vZHVsZXNfaW5fb3JkZXIsICJeQUUiKQpuYW1lcyhsYWJlbHNfZG90cGxvdCkgPC0gbW9kdWxlc19pbl9vcmRlcgoKcGxvdF9jbHVzdGVycyA8LSBjKCJwcm9nZW5pdG9ycyIsICJuZXVyb25zIiwgIlJQIiwgIkZQIiwgInBlcmljeXRlcyIsICJPUEMiLCAiTUZPTCIsICJtaWNyb2dsaWEiLCAiYmxvb2QiKQoKbWVhbl9tb2QgPC0gZ2dwbG90KGRhdGEgPSBtZWFuX0FFLAogIGFlcygKICAgIHggPSBzdGFnZSwKICAgIHkgPSBBRSwKICAgIGNvbG9yID0gZmFjdG9yKGJyb2FkLCBsZXZlbHMgPSBwbG90X2NsdXN0ZXJzKSwKICAgIGdyb3VwID0gYnJvYWQsCiAgICBsYWJlbCA9IGFubm90YXRpb24KICAgICkKICApICsKICBnZW9tX2xpbmUoKSArCiAgZ2VvbV9wb2ludCgpICsKICBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzID0gY2x1c3RfY29sJGNvbG9yW21hdGNoKHBsb3RfY2x1c3RlcnMsIGNsdXN0X2NvbCRicm9hZCldKSArCiAgdGhlbWVfYncoKSArCiAgIyBmYWNldCB3cmFwIHdpdGggcmVvcmRlcmVkIGZhY3RvcnMKICBmYWNldF93cmFwKHZhcnMoZmFjdG9yKG1vZHVsZSwgbGV2ZWxzID0gdW5pcXVlKG1lYW5fQUUkbW9kdWxlKVttb2R1bGVfb3JkZXJdKSksCiAgICAgICAgICAgICBzY2FsZXMgPSAiZnJlZV95IiwKICAgICAgICAgICAgIG5yb3cgPSA0LAogICAgICAgICAgICAgbmNvbCA9IDYpICsKICBsYWJzKGNvbG9yID0gImJyb2FkIikgKwogIGdndGl0bGUoIkF2ZXJhZ2UgbW9kdWxlIGV4cHJlc3Npb24gYnkgc3RhZ2UiKQoKcGxvdGx5OjpnZ3Bsb3RseShtZWFuX21vZCkKCnBkZigifi9zcGluYWxfY29yZF9wYXBlci9maWd1cmVzL0ZpZ18xX21lYW5fbW9kX0FFLnBkZiIsIGhlaWdodCA9IDcsIHdpZHRoID0gMTIpCiNQbG90IHNwbGl0IHRzbmUKbWVhbl9tb2QKYGBgCgojIFZsblBsb3RzIG9mIGF2Zy4gbW9kdWxlIGV4cC4gYnkgc3RhZ2UgYW5kIHNldXJhdCBjbHVzdGVyIAoKIyMgY29sb3JlZCBieSBtb2R1bGUKCmBgYHtyIGF2ZXJhZ2UtbW9kdWxlLWV4cHJlc3Npb24tcGVyLWNsdXN0ZXIsIGZpZy53aWR0aD0xMCwgZmlnLmhlaWdodD0yMH0KCiMgcmVvcmRlciBzZXVyYXQgY2x1c3RlcnMKZm9yIChpIGluIHNlcShteS5zZXMpKSB7CiAgbXkuc2VzW1tpXV0kc2V1cmF0X2NsdXN0ZXJzIDwtIGZhY3RvcigKICBteS5zZXNbW2ldXSRzZXVyYXRfY2x1c3RlcnMsCiAgbGV2ZWxzID0gbGV2ZWxzKG15LnNlc1tbaV1dJHNldXJhdF9jbHVzdGVycylbYXMuaW50ZWdlcihzdHJfZXh0cmFjdChvcmRfbGV2ZWxzW1tpXV0sICJcXGR7MSwyfSQiKSldCiAgKQoKfQoKdnBsb3RzIDwtIGxpc3QoKQoKZm9yIChpIGluIHNlcShteS5zZXMpKSB7CiAgCiAgbW9kcyA8LSBjb2xuYW1lcyhteS5zZXNbW2ldXVtbXV0pW2dyZXAoIl5BRSIsY29sbmFtZXMobXkuc2VzW1tpXV1bW11dKSldCiAgCiAgdnBsb3RzW1tpXV0gPC0gVmxuUGxvdCgKICAgICAgICBteS5zZXNbW2ldXSwKICAgICAgICBmZWF0dXJlcyA9IG1vZHNbbW9kdWxlX29yZGVyXSwKICAgICAgICBncm91cC5ieSA9ICJzZXVyYXRfY2x1c3RlcnMiLAogICAgICAgIHN0YWNrID0gVFJVRSwgZmxpcCA9IFRSVUUsCiAgICAgICAgY29scyA9IHN1YnN0cmluZyhtb2RzLCAzKVttb2R1bGVfb3JkZXJdKSArCiAgICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAibm9uZSIpICsKICAgIGdlb21faGxpbmUoeWludGVyY2VwdCA9IDAsIGx0eSA9ICJkYXNoZWQiKQogIAp9Cgp2cGxvdHNbWzFdXQp2cGxvdHNbWzJdXQp2cGxvdHNbWzNdXQoKcGRmKCJ+L3NwaW5hbF9jb3JkX3BhcGVyL2ZpZ3VyZXMvRmlnXzFfQUVfYnlfY2x1c3Rlcl9tb2Rjb2wucGRmIiwgaGVpZ2h0ID0gMjAsIHdpZHRoID0gMTApCnZwbG90c1tbMV1dCnZwbG90c1tbMl1dCnZwbG90c1tbM11dCmBgYAoKIyMgY29sb3JlZCBieSBjZWxsIHR5cGUKCmBgYHtyIHZsbi1ieS1jbHVzdGVyLCBmaWcud2lkdGg9MTAsIGZpZy5oZWlnaHQ9MjB9CmNsdXN0X2NvbCA8LSByZWFkLmNzdigifi9zcGluYWxfY29yZF9wYXBlci9hbm5vdGF0aW9ucy9icm9hZF9jbHVzdGVyX21hcmtlcl9jb2xvcnMuY3N2IikKCnZwbG90c19pZCA8LSBsaXN0KCkKCmZvciAoaSBpbiBzZXEobXkuc2VzKSkgewogICAgCiAgbW9kcyA8LSBjb2xuYW1lcyhteS5zZXNbW2ldXVtbXV0pW2dyZXAoIl5BRSIsY29sbmFtZXMobXkuc2VzW1tpXV1bW11dKSldCiAgCiAgdnBsb3RzX2lkW1tpXV0gPC0gVmxuUGxvdCgKICAgIG15LnNlc1tbaV1dLAogICAgZmVhdHVyZXMgPSBtb2RzW21vZHVsZV9vcmRlcl0sCiAgICBncm91cC5ieSA9ICJzZXVyYXRfY2x1c3RlcnMiLAogICAgc3RhY2sgPSBUUlVFLCBmbGlwID0gVFJVRSwKICAgIGZpbGwuYnkgPSAiaWRlbnQiLAogICAgY29scyA9IGNvbF90YWJsZVtbaV1dJGNvbG9yW2FzLmludGVnZXIoc3RyX2V4dHJhY3Qob3JkX2xldmVsc1tbaV1dLCAiXFxkezEsMn0kIikpXSkgKwogICAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiKSArCiAgICBnZW9tX2hsaW5lKHlpbnRlcmNlcHQgPSAwLCBsdHkgPSAiZGFzaGVkIikKfQoKdnBsb3RzX2lkW1sxXV0KdnBsb3RzX2lkW1syXV0KdnBsb3RzX2lkW1szXV0KCnBkZigifi9zcGluYWxfY29yZF9wYXBlci9maWd1cmVzL0ZpZ18xX0FFX2J5X2NsdXN0ZXJfY2x1Y29sLnBkZiIsIGhlaWdodCA9IDIwLCB3aWR0aCA9IDEwKQp2cGxvdHNfaWRbWzFdXQp2cGxvdHNfaWRbWzJdXQp2cGxvdHNfaWRbWzNdXQpgYGAKCiMgTU4gbW9kdWxlcwoKRm9yIHRoZSBmaWd1cmVzIHdlIHNwZWNpZmljYWxseSBzZWxlY3QgdGhlIHR3byBNTiBtb2R1bGVzIGFuZCBwbG90IHRoZW0gYXMgVmxuIHBsb3RzLgoKYGBge3IsIGZpZy53aWR0aD0yMCwgZmlnLmhlaWdodD01fQp0bXAgPC0gYXZnLm1vZC5laWdlbmdlbmVzIAogIAppZGVudGljYWwocm93bmFtZXModG1wKSwgY29sbmFtZXMobXkuc2VjKSkKIyBhZGQgbWV0YSBkYXRhIHRvIHRoZSBpbnQgc2V1cmF0IG9iamVjdApteS5zZWMgPC0gQWRkTWV0YURhdGEobXkuc2VjLCB0bXApCm15LnNlYyA8LSBBZGRNZXRhRGF0YShteS5zZWMsIG15Lm1ldGFtW2MoImZpbmUiLCAic2FtcGxlX2NlbGx0eXBlIildKQoKY3VzdG9tX29yZGVyIDwtIGMocGFzdGUobmFtZXMob3JkX2xldmVscylbMV0sIG9yZF9sZXZlbHNbWzFdXSwgc2VwID0gJ18nKSwKICAgICAgICAgICAgICAgICAgcGFzdGUobmFtZXMob3JkX2xldmVscylbMl0sIG9yZF9sZXZlbHNbWzJdXSwgc2VwID0gJ18nKSwKICAgICAgICAgICAgICAgICAgcGFzdGUobmFtZXMob3JkX2xldmVscylbM10sIG9yZF9sZXZlbHNbWzNdXSwgc2VwID0gJ18nKSkKCm15LnNlYyRzYW1wbGVfY2VsbHR5cGUgPC0gZmFjdG9yKAogIG15LnNlYyRzYW1wbGVfY2VsbHR5cGUsCiAgbGV2ZWxzID0gY3VzdG9tX29yZGVyCiAgKQoKdmxuX2luZCA8LSBWbG5QbG90KG15LnNlYywKICAgICAgICAgICAgICAgICAgIGZlYXR1cmVzID0gYygiQUVkYXJrcmVkIiwgIkFFZ3JlZW4iKSwKICAgICAgICAgICAgICAgICAgIGdyb3VwLmJ5ID0gInNhbXBsZV9jZWxsdHlwZSIsCiAgICAgICAgICAgICAgICAgICBzdGFjayA9IFRSVUUsCiAgICAgICAgICAgICAgICAgICBmbGlwID0gVFJVRSwKICAgICAgICAgICAgICAgICAgIGNvbHMgPSBjKCJkYXJrcmVkIiwgImdyZWVuIikscHQuc2l6ZSA9IDEKICAgICAgICAgICAgICAgICAgKSArCiAgICBOb0xlZ2VuZCgpCgp2bG5faW5kCnBkZigifi9zcGluYWxfY29yZF9wYXBlci9maWd1cmVzL0ZpZ18xX0FFX01Ocy5wZGYiLCBoZWlnaHQgPSA3LCB3aWR0aCA9IDIwKQp2bG5faW5kCmBgYAoKYGBge3J9ClZsblBsb3QoCiAgICBteS5zZWMsCiAgICBmZWF0dXJlcyA9IG1vZHNbbW9kdWxlX29yZGVyXSwKICAgIGdyb3VwLmJ5ID0gInNhbXBsZV9jZWxsdHlwZSIsCiAgICBzdGFjayA9IFRSVUUsIGZsaXAgPSBUUlVFLAogICAgY29scyA9IHN1YnN0cmluZyhtb2RzLCAzKVttb2R1bGVfb3JkZXJdKSArCiAgICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAibm9uZSIpICsKICAgIGdlb21faGxpbmUoeWludGVyY2VwdCA9IDAsIGx0eSA9ICJkYXNoZWQiKQoKCgpwZGYoIn4vc3BpbmFsX2NvcmRfcGFwZXIvZmlndXJlcy9GaWdfMV9BRV9ieV9jbHVzdGVyX2ludGVncmF0ZWRfZGF0YS5wZGYiLCBoZWlnaHQgPSAyMCwgd2lkdGggPSAzMCkKVmxuUGxvdCgKICAgIG15LnNlYywKICAgIGZlYXR1cmVzID0gbW9kc1ttb2R1bGVfb3JkZXJdLAogICAgZ3JvdXAuYnkgPSAic2FtcGxlX2NlbGx0eXBlIiwKICAgIHN0YWNrID0gVFJVRSwgZmxpcCA9IFRSVUUsCiAgICBjb2xzID0gc3Vic3RyaW5nKG1vZHMsIDMpW21vZHVsZV9vcmRlcl0pICsKICAgIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIikgKwogICAgZ2VvbV9obGluZSh5aW50ZXJjZXB0ID0gMCwgbHR5ID0gImRhc2hlZCIpCgoKYGBgCgojIGF2Zy4gbW9kdWxlIGV4cC4gYnkgc3RhZ2UKCmBgYHtyIEFFLWJ5LXN0YWdlLCBmaWcud2lkdGg9MjAsIGZpZy5oZWlnaHQ9MjB9CnYxIDwtIFZsblBsb3QobXkuc2VjLAogICAgICAgIGZlYXR1cmVzID0gbW9kc1ttb2R1bGVfb3JkZXJdLCAKICAgICAgICBncm91cC5ieSA9ICJvcmlnLmlkZW50IikKdjEKCnBkZigifi9zcGluYWxfY29yZF9wYXBlci9maWd1cmVzL0ZpZ18xX0FFX2J5X3N0YWdlLnBkZiIsIGhlaWdodCA9IDIwLCB3aWR0aCA9IDIwKQp2MQoKYGBgCgoKYGBge3J9CiMgRGF0ZSBhbmQgdGltZSBvZiBSZW5kZXJpbmcKU3lzLnRpbWUoKQoKc2Vzc2lvbkluZm8oKQpgYGAKCg==